/* syscall.c : syscalls
 */

#include <kernel.h>
#include <stdarg.h>


static Syscall_args sys_args; //Statically allocated pointer to arguments for a create syscall

/* 
*	Generic syscall mechanism, taking an integer representing the type (see kernel.h for 
*	symbolic definitions) and a variable number of arguments. Positions those arguments in
*	memory, places the call type and a pointer to the arguments in eax/ecx, then initiates 
*	a trap into the kernel
*/
extern int syscall(int call, ...){
	int ret;
	va_list args;
	va_start(args, call);
	switch(call){
		case(REQ_CREATE):{
			sys_args.arg0 = (void (*)(void))va_arg(args, int);
			sys_args.arg1 = va_arg(args, int); 
			break;
		}
		case(REQ_PUTS):{
			sys_args.arg0 = va_arg(args, char*);
			break;
		}

		case(REQ_SLEEP):{
			sys_args.arg0 = va_arg(args, unsigned int);			
			break;
		}
		case(REQ_SEND):
			sys_args.arg0 = va_arg(args, int);
			sys_args.arg1 = va_arg(args, void*);
			sys_args.arg2 = va_arg(args, int);
			break;
		case(REQ_RECV):
			sys_args.arg0 = va_arg(args, unsigned int*);
			sys_args.arg1 = va_arg(args, void*);
			sys_args.arg2 = va_arg(args, int);
			break;
		case(REQ_KILL):
			sys_args.arg0 = va_arg(args, int);
			sys_args.arg1 = va_arg(args, int);
			break;
		case(REQ_SIGHANDLER):
			sys_args.arg0 = va_arg(args, int);
			sys_args.arg1 = (void (*)(void*))va_arg(args, int); 
			break;
		case(REQ_SIGRET):
			sys_args.arg0 = va_arg(args, void*);
			sys_args.arg1 = va_arg(args, int);
			sys_args.arg2 = va_arg(args, unsigned int);
			break;
		
		/* <device cases> */
		case REQ_OPEN:
		case REQ_CLOSE:
		    sys_args.arg0 = va_arg(args, int);  // devnum or fd
		    break;
		case REQ_WRITE:
		case REQ_READ:
		    sys_args.arg0 = va_arg(args, int);      // fd
		    sys_args.arg1 = va_arg(args, void*);    // buf
		    sys_args.arg2 = va_arg(args, int);      // buflen
		    break;
		case REQ_IOCTL:
		    sys_args.arg0 = va_arg(args, int);              // fd
		    sys_args.arg1 = va_arg(args, unsigned long);    // cmd
		    break;
		/* </device cases> */
		    
		case(REQ_SIGWAIT):
		case(REQ_GETPID):
		case(REQ_YIELD): 
		case(REQ_STOP):
			break;
	}
	va_end(args);
	int argsloc = &sys_args;
	__asm __volatile("movl %[call], %%eax\n\t"	
			"movl %[argsloc], %%ecx\n\t"
			"int $63\n\t"
			"movl %%eax, %[ret]"
			:[call] "=m" (call), [argsloc] "=m" (argsloc), [ret] "=m" (ret)
			:
			:"%eax"
			);
	return ret;
}
extern int syscreate(void (*fn)(), int stack){
	return syscall(REQ_CREATE, fn, stack);
}
extern int sysyield(void){
	return syscall(REQ_YIELD);
}
extern int sysstop(void){
	return syscall(REQ_STOP);
}
extern unsigned int sysgetpid(void){
	return syscall(REQ_GETPID);
}
extern void sysputs( char *str ){
	syscall(REQ_PUTS, str);
}
extern void syssleep(int ms){
	syscall(REQ_SLEEP, ms);
}
extern int syssend(int dest_pid, void* buf, int len){
	return syscall(REQ_SEND, dest_pid, buf, len);
}
extern int sysrecv(unsigned int* from_pid, void* buf, int len){
	return syscall(REQ_RECV, from_pid, buf, len);
}
extern int syssighandler(int signal, void (*handler)(void*)){
	return syscall(REQ_SIGHANDLER, signal, handler);
}
extern int syskill(int PID, int signalNumber){
	return syscall(REQ_KILL, PID, signalNumber);
}
extern int syssigwait(void){
	return syscall(REQ_SIGWAIT);
}
extern void sigreturn(void *osp, int oldrc, unsigned int oldignoremask){
	return syscall(REQ_SIGRET, osp, oldrc, oldignoremask);
}

extern int sysopen(int devnum){
    return syscall(REQ_OPEN, devnum);
}

extern int sysclose(int fd){
    return syscall(REQ_CLOSE, fd);
}

extern int syswrite(int fd, void* buf, int buflen){
    return syscall(REQ_WRITE, fd, buf, buflen);
}

extern int sysread(int fd, void* buf, int buflen){
    return syscall(REQ_READ, fd, buf, buflen);
}

extern int sysioctl(int fd, unsigned long cmd, ...){
    
    va_start(__sysioctl_args, cmd);
    
    return syscall(REQ_IOCTL, fd, cmd);
}
